package ms.unicorn.modellanguage;

import java.util.HashMap;
import java.util.Map;

import ms.unicorn.Element;
import ms.unicorn.diagram.edit.parts.EntityEditPart;
import ms.unicorn.diagram.edit.parts.RelationEditPart;
import ms.unicorn.language.languagedef.ElementDef;
import ms.unicorn.language.languagedef.ElementDefUtil;
import ms.unicorn.language.languagedef.FigureContainer;
import ms.unicorn.language.languagedef.LabelViewDef;
import ms.unicorn.language.languagedef.LanguageDefUtil;
import ms.unicorn.language.languagedef.Policies;
import ms.unicorn.language.languagedef.PolicyDef;
import ms.unicorn.language.languagedef.ViewDef;
import ms.unicorn.language.languagedef.ViewDefUtil;
import ms.unicorn.language.languagedef.XReferenceExpression;
import ms.unicorn.util.ErrorHandler;
import ms.unicorn.util.FigureConstant;
import ms.unicorn.util.ViewUtil;

import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.transaction.util.TransactionUtil;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.EditPolicy;
import org.eclipse.gmf.runtime.common.core.command.CommandResult;
import org.eclipse.gmf.runtime.common.core.command.ICommand;
import org.eclipse.gmf.runtime.common.ui.services.parser.IParser;
import org.eclipse.gmf.runtime.diagram.core.preferences.PreferencesHint;
import org.eclipse.gmf.runtime.emf.core.util.EMFCoreUtil;
import org.eclipse.gmf.runtime.emf.type.core.IElementType;
import org.eclipse.gmf.runtime.emf.type.core.commands.ConfigureElementCommand;
import org.eclipse.gmf.runtime.emf.type.core.edithelper.AbstractEditHelperAdvice;
import org.eclipse.gmf.runtime.emf.type.core.requests.ConfigureRequest;
import org.eclipse.gmf.runtime.emf.type.core.requests.IEditCommandRequest;
import org.eclipse.gmf.runtime.notation.View;

public abstract class LanguageElementEditHelperAdvice extends AbstractEditHelperAdvice implements IElementEditHelperAdvice, FigureConstant {
	
	public static final String ENTITY_ID = "ms.unicorn.Entity";
	public static final String RElATION_ID = "ms.unicorn.Relation";
	
	protected Map<String, Class<? extends EditPart>> editPartMap = new HashMap<String, Class<? extends EditPart>>();
	
	public LanguageElementEditHelperAdvice() {
		super();
		editPartMap.put(ENTITY_ID, EntityEditPart.class);
		editPartMap.put(RElATION_ID, RelationEditPart.class);
	}

	@Override
	public ICommand getAfterEditCommand(IEditCommandRequest request) {
		if (request instanceof ConfigureRequest) {
			request.setParameter(IEditCommandRequest.REPLACE_DEFAULT_COMMAND, Boolean.TRUE);
			return new ConfigureElementCommand((ConfigureRequest) request) {
		
				@Override
				protected CommandResult doExecuteWithResult(
						IProgressMonitor monitor, IAdaptable info)
						throws ExecutionException {
					
					ConfigureRequest request = (ConfigureRequest) this.getRequest();
					Element domainElement = (Element) request.getElementToConfigure();
					IElementType type = request.getTypeToConfigure();
					if (type instanceof HintedSpecializationType)
						((HintedSpecializationType) type).getElement().getLanguage().configElement(domainElement, request);
					return CommandResult.newOKCommandResult();
				}
			};
		}
		return null;
	}

	@Override
	public EditPart createEditPart(EditPart context, Object model) {
		if (model instanceof View) {
			Class<? extends EditPart> editPartClass = null;
			View view = (View)model;
			ViewDef viewDef = ViewDefUtil.resolveViewDef(view);
			if (viewDef != null) 
				editPartClass = ViewDefUtil.getViewEditPart(viewDef);
			else 
				editPartClass = editPartMap.get(view.getType());

			if (editPartClass != null) {
				try {
					return editPartClass.getConstructor(View.class).newInstance(view);
				} catch (Exception e) {
					ErrorHandler.handleException(e);
				} 
			}
		}
		return null;
	}

	@Override
	public EditPolicy createPrimaryDragEditPolicy(Element element, EditPart editPart) {
		return null;
	}

	protected EObject getSemanticElement(IAdaptable semanticAdapter) {
		if (semanticAdapter == null) {
			return null;
		}
		EObject eObject = (EObject) semanticAdapter.getAdapter(EObject.class);
		if (eObject != null) {
			return EMFCoreUtil.resolve(
					TransactionUtil.getEditingDomain(eObject), eObject);
		}
		return null;
	}
	
	@Override
	public View createView(LanguageElement element, IAdaptable semanticAdapter, View containerView,
			String semanticHint, int index, boolean persisted, PreferencesHint preferencesHint) {
		Element domainElement = (Element)getSemanticElement(semanticAdapter);
		
		ViewDef viewDef = LanguageDefUtil.resolveDefinition(element.getDefinition().getView());
		View view = ViewDefUtil.resolveView(viewDef, domainElement, preferencesHint, null);
		
		ViewUtil.insertChildView(containerView, view, index, persisted);
		
		return view;
	}
	
	public FigureContainer createFigure(LanguageElement element, Element domainObj, EditPart editpart) {
		ElementDef def = element.getDefinition();		
		return ElementDefUtil.createFigure(element, def, editpart);
	}
	
	@Override
	public IParser getParser(ViewDef definition, Element obj, LanguageElement le) {
		return ViewDefUtil.getParser((LabelViewDef) definition);
	}
	
	@Override
	public void createDefaultEditPolicies(LanguageElement lang, Element element, EditPart editpart) {
		Policies policies = lang.getDefinition().getPolicies();
		if (policies != null)
		 	for(XReferenceExpression exp: policies.getPolicyExp()) {
		 		PolicyDef policy = LanguageDefUtil.resolveDefinition(exp);
		 		if (!policy.isLayoutPolicy()) {
		 			EditPolicy policyIns = LanguageDefUtil.createFromDefinition(policy, null);
		 			String role = policy.getPolicy();
 					if (role == null)
 						role = EditPolicy.GRAPHICAL_NODE_ROLE;
		 			if (policyIns != null)
		 				editpart.installEditPolicy(role, (EditPolicy) policyIns);
		 		}
		 	}
	}
}